// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
// SPDX-License-Identifier: GPL-3.0-only

package manifest_impl

import (
	"encoding/json"
	"fmt"
	"io"

	"gitcode.com/openseaotter/so_proto_gen_go.git/image_api"
	"gitcode.com/openseaotter/so_server/storage"
)

//application/vnd.oci.image.manifest.v1+json
//https://github.com/opencontainers/image-spec/blob/main/manifest.md

type OciManifest struct {
	SchemaVersion int               `json:"schemaVersion"`
	MediaType     string            `json:"mediaType"`
	ArtifactType  string            `json:"artifactType"`
	Config        ManifestConfig    `json:"config"`
	Layers        []ImageLayer      `json:"layers"`
	Subject       *ManifestConfig   `json:"subject,omitempty"`
	Annotations   map[string]string `json:"annotations,omitempty"`
}

func (om *OciManifest) ToProto(architecture, osStr string) (*image_api.ImageManifestList_ManifestItem, error) {
	retItem := &image_api.ImageManifestList_ManifestItem{
		Manifest: &image_api.ImageManifest{
			Layers: []*image_api.ImageManifest_Layer{},
		},
		Architecture: architecture,
		Os:           osStr,
	}
	r, err := storage.BlobStore.Open(om.Config.Digest)
	if err != nil {
		return nil, err
	}
	defer r.Close()

	data, err := io.ReadAll(r)
	if err != nil {
		return nil, err
	}
	if om.Config.MediaType == _DOCKER_IMAGE_TYPE {
		dc := &DockerImageSpec{}
		err = json.Unmarshal(data, dc)
		if err != nil {
			return nil, err
		}
		retItem.Manifest.Config, err = dc.ToProto()
		if err != nil {
			return nil, err
		}
	} else if om.Config.MediaType == _OCI_IMAGE_TYPE {
		oc := &OciImageSpec{}
		err = json.Unmarshal(data, oc)
		if err != nil {
			return nil, err
		}
		retItem.Manifest.Config, err = oc.ToProto()
		if err != nil {
			return nil, err
		}
	} else {
		return nil, fmt.Errorf("unkwown mediaType %s", om.Config.MediaType)
	}
	for _, layer := range om.Layers {
		retItem.Manifest.Layers = append(retItem.Manifest.Layers, &image_api.ImageManifest_Layer{
			MediaType: layer.MediaType,
			Digest:    layer.Digest,
			Size:      uint32(layer.Size),
		})
	}
	return retItem, nil
}

func (om *OciManifest) CollectBlobId(result *CollectBlobIdResult) {
	result.BlobIdList = append(result.BlobIdList, om.Config.Digest)
	for _, layer := range om.Layers {
		result.BlobIdList = append(result.BlobIdList, layer.Digest)
	}
}

//application/vnd.oci.image.index.v1+json
//https://github.com/opencontainers/image-spec/blob/main/image-index.md

type OciManifestList struct {
	SchemaVersion int                `json:"schemaVersion"`
	MediaType     string             `json:"mediaType"`
	ArtifactType  string             `json:"artifactType"`
	Manifests     []ManifestListItem `json:"manifests"`
}

func (ol *OciManifestList) ToProto() ([]*image_api.ImageManifestList_ManifestItem, error) {
	retList := []*image_api.ImageManifestList_ManifestItem{}

	for _, m := range ol.Manifests {
		r, err := storage.BlobStore.Open(m.Digest)
		if err != nil {
			return nil, err
		}
		defer r.Close()
		data, err := io.ReadAll(r)
		if err != nil {
			return nil, err
		}
		if m.MediaType == DOCKER_MANIFEST_TYPE {
			dm := &DockerManifest{}
			err = json.Unmarshal(data, dm)
			if err != nil {
				return nil, err
			}
			item, err := dm.ToProto(m.Platform.Architecture, m.Platform.Os)
			if err != nil {
				return nil, err
			}
			retList = append(retList, item)
		} else if m.MediaType == OCI_MANIFEST_TYPE {
			om := &OciManifest{}
			err = json.Unmarshal(data, om)
			if err != nil {
				return nil, err
			}
			item, err := om.ToProto(m.Platform.Architecture, m.Platform.Os)
			if err != nil {
				return nil, err
			}
			retList = append(retList, item)
		} else {
			return nil, fmt.Errorf("unkwown mediaType %s", m.MediaType)
		}
	}
	return retList, nil
}

func (ol *OciManifestList) CollectBlobId(result *CollectBlobIdResult) error {
	for _, m := range ol.Manifests {
		result.BlobIdList = append(result.BlobIdList, m.Digest)
		r, err := storage.BlobStore.Open(m.Digest)
		if err != nil {
			return err
		}
		defer r.Close()
		data, err := io.ReadAll(r)
		if err != nil {
			return err
		}
		if m.MediaType == DOCKER_MANIFEST_TYPE {
			dm := &DockerManifest{}
			err = json.Unmarshal(data, dm)
			if err != nil {
				return err
			}
			dm.CollectBlobId(result)
		} else if m.MediaType == OCI_MANIFEST_TYPE {
			om := &OciManifest{}
			err = json.Unmarshal(data, om)
			if err != nil {
				return err
			}
			om.CollectBlobId(result)
		}
	}
	return nil
}
